/* ex: set tabstop=4 shiftwidth=4 expandtab: */
/* modified from ftp://www.tug.org/vm/base64-encode.c
 * and ftp://www.tug.org/vm/base64-decode.c  
 */

#include <stdio.h>

static unsigned char alphabet[64] = 
	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

int
base64_encode(char *dst, const char *src, int slen)
{
	int c, bits, cnt, len;

	if (slen <= 0)
		return 0;

	bits = cnt = len = 0;

	while (slen--) {
		c = (unsigned char) *src++;
		bits += c;
		cnt++;
		if (cnt == 3) {
			dst[len++] = alphabet[bits >> 18];
			dst[len++] = alphabet[(bits >> 12) & 0x3F];
			dst[len++] = alphabet[(bits >> 6) & 0x3F];
			dst[len++] = alphabet[bits  & 0x3F];
			bits = 0;
			cnt = 0;
		} else {
			bits <<= 8;
		}
	}

	if (cnt != 0) {
		bits <<= 16 - (8 * cnt);
		dst[len++] = alphabet[bits >> 8];
		dst[len++] = alphabet[(bits >> 12) & 0x3F];
		if (cnt == 1) {
			dst[len++] = '=';
			dst[len++] = '=';
		} else {
			dst[len++] = alphabet[(bits >> 6) & 0x3F];
			dst[len++] = '=';
		}
	}

	dst[len] = '\0';

	return len;
}


static char inalphabet[256];
static char decoder[256];

void
base64_init(void)
{
	int i;

	for (i = sizeof(alphabet) - 1; i >= 0; i--) {
		inalphabet[alphabet[i]] = 1;
		decoder[alphabet[i]] = i;
	}
}

/*
 * returns len of decoded string (not including last '\0')
 */
int
base64_decode(char *dst, const char *src)
{
	int bits, c, cnt;
	char *start = dst;

	cnt = bits = 0;

	while ((c = *src++) != '\0') {
		if (c == '=')
			break;
		if (c > 255 || !inalphabet[c])
			continue;
		bits += decoder[c];
		cnt++;
		if (cnt == 4) {
			*dst++ = bits >> 16;
			*dst++ = (bits >> 8) & 0xFF;
			*dst++ = bits & 0xFF;
			bits = 0;
			cnt = 0;
		} else {
			bits <<= 6;
		}
	}

	if (c == '\0') {
		if (cnt)
			return 0;
	} else {
		switch (cnt) {
		case 1:
			return 0;

		case 2:
			*dst++ = bits >> 10;
			break;

		case 3:
			*dst++ = bits >> 16;
			*dst++ = (bits >> 8) & 0xFF;
			break;
		}
		*dst = '\0';
	}

	return dst - start;
}

#ifdef TEST 
int
main(void)
{
#if 0
	char *s = "REVTQ1JJQkUgcnRzcDovLzE5Mi4xNjguMS42Mzo1MDI2L0RTUC92YnI9NTAwMDAwLnZtYnI9ODUwMDAwLnZmcj0zMC52dz0zNTIudmg9MjQwLnZmbD0weDExOS5hYnI9NDgwMDAuYW1icj00ODAwMC5hc3I9MjQwMDAuYWZsPTB4NTE2LnZ0eXBlPTIuIFJUU1AvMS4wDQpDU2VxOiAxDQpBY2NlcHQ6IGFwcGxpY2F0aW9uL3NkcA0KVQ==";

	char *s2 = "c2VyLUFnZW50OiBMRlggU3RyZWFtaW5nIENsaWVudA0KDQo=";
#endif
	char result[1024];
	char result2[1024];
	int i, len;
	char *s = "AAAAAAIAAAADAAAAQUxJVkUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";

	base64_init();
	len = base64_decode(result, s);
	//printf("%s\n", result);
	printf("%d\n", len);
	for (i = 0; i < 44; i++)
		printf("%02X ", result[i]);
	printf("\n");
	len = base64_encode(result2, result, 44);
	//base64_decode(s2, result);
	printf("%d %s\n", len, result2);
	printf("%d %d\n", strlen(s), strcmp(s, result2));

	return 0;

}
#endif
